package com.nirvana.generate;

import com.nirvana.config.PluginConfig;
import com.nirvana.data.DependenceData;
import com.nirvana.factory.ItemFactory;
import com.nirvana.item.CommonItem;
import com.nirvana.item.DefPropertyItem;
import com.nirvana.type.ItemType;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;

import static com.nirvana.config.PluginConfig.PLUGIN_GENERATE_FLAG;

public class XmlDefClassGenerator implements GenerateInterface {
    private static XmlDefClassGenerator INSTANCE;
    private final CommonItem commonItem = ItemFactory.getCommonItem(ItemType.DefPropertyItem);

    private boolean hasImportTool = false;

    private XmlDefClassGenerator() {

    }

    public static XmlDefClassGenerator getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new XmlDefClassGenerator();
        }
        return INSTANCE;
    }

    @Override
    public void generate() {
        StringBuilder content = new StringBuilder();
        StringBuilder packageContent = new StringBuilder();
        StringBuilder importContent = new StringBuilder();
        StringBuilder classContent = new StringBuilder();
        StringBuilder fieldContent = new StringBuilder();
        StringBuilder resolveStringMethodContent = new StringBuilder();
        StringBuilder resolveMapBeanMethodContent = new StringBuilder();

        Set<String> hasImport = new HashSet<>();
        hasImportTool = false;

        generatePackage(packageContent);
        commonItem.itemList.forEach(item -> {
            if (item instanceof DefPropertyItem defPropertyItem) {
                generateImport(importContent, defPropertyItem, hasImport);
                generateField(fieldContent, defPropertyItem);
                generateResolveStringMethod(resolveStringMethodContent, defPropertyItem);
                generateResolveMapBeanMethod(resolveMapBeanMethodContent, defPropertyItem);
            }
        });
        if (PluginConfig.getInstance().extendsGroupDef()) {
            importContent.append("""
                    import com.nirvana.xml.GroupDef
                    """);
            resolveMapBeanMethodContent.append("""
                            initGroup(mapBean)
                    """);
        }
        if (!resolveMapBeanMethodContent.isEmpty()) importContent.append("import com.nirvana.core.MapBean\n");
        generateClass(classContent, fieldContent, resolveStringMethodContent, resolveMapBeanMethodContent);
        content.append(packageContent).append(importContent).append(classContent);

        String className = commonItem.textField.getText();
        String defBasePath = PluginConfig.getInstance().defBasePath();
        Path basePath = Paths.get(defBasePath);
        try {
            if (Files.notExists(basePath)) {
                Files.createDirectories(basePath);
            }
            Path path = Paths.get(defBasePath + "/" + className + ".scala");
            Files.writeString(path, content.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void generatePackage(StringBuilder packageContent) {
        packageContent.append("""
                package %s
                
                """.formatted(PluginConfig.getInstance().defDotPackage()));
    }
    private void generateImport(StringBuilder importContent, DefPropertyItem item, Set<String> hasImport) {
        if (!item.isResolve()) return;
        // 导入工具类
        if (!hasImportTool) {
            importContent.append("""
                import %s._
                """.formatted(PluginConfig.getInstance().getConvertUtilPackage()));
            hasImportTool = true;
        }

        String propertyType = item.getPropertyType();
        String[] types = propertyType.replaceAll("[^a-zA-Z0-9]", " ").split("\\s+");
        for (String type : types) {
            if (hasImport.contains(type) || StringUtils.isBlank(type)) continue;

            DependenceData dependenceDataByType = PluginConfig.getInstance().getDependenceDataByType(type);

            if (dependenceDataByType == null) continue;

            // 导入依赖类
            if (dependenceDataByType.canImport()) {
                importContent.append("""
                import %s
                """.formatted(dependenceDataByType.getFullClassName()));
            }
            hasImport.add(type);
        }

    }

    private void generateClass(StringBuilder classContent, StringBuilder fieldContent, StringBuilder resolveStringMethodContent, StringBuilder resolveMapBeanMethodContent) {
        String extendsGroupDef = "";
        if (PluginConfig.getInstance().extendsGroupDef()) {
            extendsGroupDef = "extends GroupDef ";
        }
        classContent.append("""
                
                class %s %s{
                %s
                %s
                %s
                }
                %s
                """.formatted(
                        commonItem.textField.getText(),
                        extendsGroupDef,
                        fieldContent.toString(),
                        doGenerateResolveStringMethodBody(resolveStringMethodContent),
                        doGenerateResolveMapBeanMethodBody(resolveMapBeanMethodContent),
                        PLUGIN_GENERATE_FLAG
                ));
    }

    private String doGenerateResolveStringMethodBody(StringBuilder content) {
        if (content.isEmpty()) return "";

        return """
                    /** 自定义字符串解析 */
                    private def resolveString(key: String, value: String): Unit = {
                        key match {
                %s
                            case _ =>
                        }
                    }
                """.formatted(content.toString().stripTrailing());
    }

    private String doGenerateResolveMapBeanMethodBody(StringBuilder content) {
        if (content.isEmpty()) return "";

        return """
                    /** 自定义MapBean解析 */
                    private def resolveMapBean(mapBean: MapBean): Unit = {
                %s
                    }
                """.formatted(content.toString().stripTrailing());
    }

    private void generateField(StringBuilder fieldContent, DefPropertyItem item) {
        fieldContent.append("""
                    /** %s */
                    var %s: %s = _
                """.formatted(item.getComment(), item.getPropertyName(), item.getPropertyType()));
    }

    private void generateResolveStringMethod(StringBuilder resolveStringMethodContent, DefPropertyItem item) {
        if (!item.isResolve()) return;
        int index = PluginConfig.getInstance().getDependenceDataByType(item.getPropertyType()).paramTypeIndex(item.getResolveMethodName());

        // 非String解析
        if (index != 0) return;

        resolveStringMethodContent.append("""
                            case "%s" => %s = %s(value)
                """.formatted(
                        item.getOriginPropertyName(),
                        item.getPropertyName(),
                        item.getResolveMethodName()));
    }

    private void generateResolveMapBeanMethod(StringBuilder resolveMapBeanMethodContent, DefPropertyItem item) {
        if (!item.isResolve()) return;
        int index = PluginConfig.getInstance().getDependenceDataByType(item.getPropertyType()).paramTypeIndex(item.getResolveMethodName());

        // 非MapBean解析
        if (index != 1) return;

        resolveMapBeanMethodContent.append("""
                        %s = %s(mapBean)
                """.formatted(
                item.getPropertyName(),
                item.getResolveMethodName()));
    }
}
